package com.line.service;

import com.line.model.pojo.GenerateTablePojo;
import com.line.tools.util.DBUtils;
import com.line.tools.util.DateUtils;
import com.line.tools.util.StringBasicUtils;
import com.line.tools.util.ZipUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.*;

@Service
@Transactional
public class TableService {
	private Logger log = LoggerFactory.getLogger(TableService.class);

	public static void main(String[] args) {
		System.out.println("64a3c522f503474197794b6edf517f7f".length());
	}

	public Map<String, String> generateEntity(GenerateTablePojo pojo) throws Exception {
		Connection mysql_conn = null;
		try {
			Class.forName(pojo.getDb_driver());
			mysql_conn = DriverManager.getConnection(pojo.getDb_url(), pojo.getDb_user(), pojo.getDb_pwd());
			createEntity(pojo, mysql_conn);
			createRepository(pojo, mysql_conn);
			String zipFile = pojo.getDownloadPath() + pojo.getDbName() + ".zip";
			FileOutputStream fos1 = new FileOutputStream(new File(zipFile));
			ZipUtils.toZip(pojo.getOutputPath(), fos1, true);
		} catch (Exception e) {
			log.error("同步异常！", e);
		} finally {
			mysql_conn.close();
		}
		Map<String, String> map = new HashMap<>();
		map.put("url", "https://download.59line.com/" + pojo.getDbName() + ".zip");
		return map;
	}

	private void createRepository(GenerateTablePojo pojo, Connection connection) throws Exception {
		String filename = pojo.getOutputPath() + "repository" + File.separator;
		File file = new File(filename);
		if (!file.exists()) {
			file.mkdirs();
		}
		File[] files = file.listFiles();
		for (File file2 : files) {
			file2.delete();
		}
		// 先读出所有的表
		String tableSql = "select table_name,table_comment from information_schema.tables where table_schema='" + pojo.getDbName() + "'";
		List<Map<String, Object>> listTables = DBUtils.querySql(connection, tableSql);
		for (Map<String, Object> map : listTables) {
			Set<String> listImport = new HashSet<>();
			String tableName = (String) map.get("table_name");
			log.info("实体层表：" + tableName);
			if (!StringBasicUtils.isBlank(pojo.getTableName())) {
				if (!tableName.equals(pojo.getTableName())) {
					continue;
				}
			}
			String tableComment = map.get("table_comment").toString();
			String entityName = convertJavaKey(tableName, true) + "Entity";
			String className = convertJavaKey(tableName, true) + "Repository";
			List<String> list = new ArrayList<>();
			String columnSql = "select ordinal_position,is_nullable,column_name,data_type,character_maximum_length,column_type,column_key,extra,column_comment from information_schema.columns where table_name='"
					+ tableName + "' and table_schema='" + pojo.getDbName() + "'";
			List<Map<String, Object>> listColumns = DBUtils.querySql(connection, columnSql);
			String dataType = "";
			for (Map<String, Object> mapColumns : listColumns) {
				String data_type = mapColumns.get("data_type").toString();
				String column_key = mapColumns.get("column_key").toString();
				if (column_key.equals("PRI")) {
					dataType = getDataType(data_type, listImport);
				}
			}
			list.add("package " + pojo.getResPackage() + ";");
			list.add("");
			listImport.add("import org.springframework.data.jpa.repository.JpaRepository;");
			listImport.add("import org.springframework.data.jpa.repository.JpaSpecificationExecutor;");
			listImport.add("");
			listImport.add("import " + pojo.getPackageName() + "." + entityName + ";");
			list.addAll(listImport);
			list.add("");
			list.add("/**");
			list.add(" * <p>");
			list.add(" * " + tableComment);
			list.add(" * </p>");
			list.add(" * ");
			list.add(" * @version 1.0");
			list.add(" * @author Focus");
			list.add(" * @since " + DateUtils.getDateStr());
			list.add(" */");
			list.add("public interface " + className + " extends JpaRepository<" + entityName + ", " + dataType + ">, JpaSpecificationExecutor<" + entityName
					+ "> {");
			list.add("");
			list.add("}");
			File fout = new File(filename + className + ".java");
			FileOutputStream fos = new FileOutputStream(fout);
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
			for (String string : list) {
				bw.write(string);
				bw.newLine();
			}
			bw.close();
		}

	}

	private void createEntity(GenerateTablePojo vo, Connection connection) throws Exception {
		String filename = vo.getOutputPath() + "entity" + File.separator;
		File file = new File(filename);
		if (!file.exists()) {
			file.mkdirs();
		}
		File[] files = file.listFiles();
		for (File file2 : files) {
			file2.delete();
		}
		// 先读出所有的表
		String tableSql = "select table_name,table_comment from information_schema.tables where table_schema='" + vo.getDbName() + "'";
		List<Map<String, Object>> listTables = DBUtils.querySql(connection, tableSql);
		for (Map<String, Object> map : listTables) {
			Set<String> listImport = new HashSet<>();
			String tableName = (String) map.get("table_name");
			log.info("数据层表：" + tableName);
			if (!StringBasicUtils.isBlank(vo.getTableName())) {
				if (!tableName.equals(vo.getTableName())) {
					continue;
				}
			}
			String tableComment = map.get("table_comment").toString();
			String className = convertJavaKey(tableName, true) + "Entity";
			// 1.包的引入
			listImport.add("");
			listImport.add("import com.alibaba.fastjson.JSONObject;");
			listImport.add("import javax.persistence.Column;");
			listImport.add("import javax.persistence.Entity;");
			listImport.add("import javax.persistence.Table;");
			listImport.add("import org.hibernate.annotations.DynamicInsert;");
			listImport.add("import org.hibernate.annotations.DynamicUpdate;");

			List<String> listAnnotation = new ArrayList<>();
			listAnnotation.add("");
			listAnnotation.add("/**");
			listAnnotation.add(" * <p>");
			listAnnotation.add(" * " + tableComment);
			listAnnotation.add(" * </p>");
			listAnnotation.add(" * ");
			listAnnotation.add(" * @version 1.0");
			listAnnotation.add(" * @author Focus");
			listAnnotation.add(" * @since " + DateUtils.getDateStr());
			listAnnotation.add(" */");
			listAnnotation.add("@Entity");
			listAnnotation.add("@DynamicInsert");
			listAnnotation.add("@DynamicUpdate");
			listAnnotation.add("@Table(name = \"" + tableName + "\")");
			listAnnotation.add("public class " + className + " implements java.io.Serializable {");
			listAnnotation.add("");
			listAnnotation.add("	private static final long serialVersionUID = -1L;");
			listAnnotation.add("");
			String columnSql = "select ordinal_position,is_nullable,column_name,data_type,character_maximum_length,column_type,column_key,extra,column_comment from information_schema.columns where table_name='"
					+ tableName + "' and table_schema='" + vo.getDbName() + "'";
			List<Map<String, Object>> listColumns = DBUtils.querySql(connection, columnSql);
			List<String> listParams = new ArrayList<>();
			List<String> listMethod = new ArrayList<>();
			List<String> listToString = new ArrayList<>();
			listToString.add("	public String toString() {");
			listToString.add("		JSONObject object = new JSONObject();");
			for (Map<String, Object> mapColumns : listColumns) {
				String column_name = mapColumns.get("column_name").toString();
				String data_type = mapColumns.get("data_type").toString();
				String column_key = mapColumns.get("column_key").toString();
				String extra = mapColumns.get("extra").toString();
				String is_nullable = mapColumns.get("is_nullable").toString();
				String column_type = mapColumns.get("column_type").toString();
				String column_comment = mapColumns.get("column_comment").toString();
				// id
				listParams.add("	/**");
				listParams.add("	 * " + column_comment);
				listParams.add("	 */");
				String column = "@Column(name = \"" + column_name + "\"";
				if (is_nullable.equals("NO")) {
					column += ", nullable = false";
				}
				if (column_key.equals("PRI")) {
					listParams.add("	@Id");
					column += ", unique = true";

					listImport.add("import javax.persistence.Id;");
				}
				if (column_type.indexOf("(") != -1) {
					String lengths = column_type.substring(column_type.indexOf("(") + 1, column_type.lastIndexOf(")"));
					int length = 0;
					if (lengths.indexOf(",") != -1) {
						int precision = Integer.parseInt(lengths.substring(0, lengths.indexOf(",")));
						int scale = Integer.parseInt(lengths.substring(lengths.indexOf(",") + 1, lengths.length()));
						column += ", precision = " + precision + ", scale = " + scale;
					} else {
						length = Integer.parseInt(lengths);
						column += ", length = " + length;
					}

				}
				column += ")";
				if (extra.equals("auto_increment")) {
					listParams.add("	@GeneratedValue(strategy = GenerationType.IDENTITY)");
					listImport.add("import javax.persistence.GeneratedValue;");
					listImport.add("import javax.persistence.GenerationType;");
				}
				if (data_type.equals("datetime") || data_type.equals("timestamp") || data_type.equals("date")) {
					listParams.add("	@Temporal(TemporalType.TIMESTAMP)");
					listParams.add("	@DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")");

					listImport.add("import java.util.Date;");
					listImport.add("import javax.persistence.Temporal;");
					listImport.add("import javax.persistence.TemporalType;");
					listImport.add("import org.springframework.format.annotation.DateTimeFormat;");

				}
				listParams.add("	" + column);
				String dataType = getDataType(data_type, listImport);
				String columnName = convertJavaKey(column_name, false);
				listParams.add("	private " + dataType + " " + columnName + ";");
				listParams.add("");

				// 组装方法
				String columnMethodName = convertJavaKey(column_name, true);
				// 组装set方法
				listMethod.add("	/**");
				listMethod.add("	 * 属性的公用set方法<br/>");
				listMethod.add("	 * " + column_comment);
				listMethod.add("	 */");
				listMethod.add("	public void set" + columnMethodName + "(" + dataType + " value) {");
				listMethod.add("		this." + columnName + " = value;");
				listMethod.add("    }");
				listMethod.add("");
				listMethod.add("	/**");
				listMethod.add("	 * 属性的公用get方法<br/>");
				listMethod.add("	 * " + column_comment);
				listMethod.add("	 */");
				listMethod.add("    public " + dataType + " get" + columnMethodName + "() {");
				listMethod.add("		return this." + columnName + ";");
				listMethod.add("	}");
				listMethod.add("");
				listToString.add("		object.put(\"" + columnName + "\", get" + columnMethodName + "());");
			}
			listToString.add("		return object.toJSONString();");
			listToString.add("	}");
			List<String> listEnd = new ArrayList<>();
			listEnd.add("package " + vo.getPackageName() + ";");
			listEnd.addAll(listImport);
			listEnd.addAll(listAnnotation);
			listEnd.addAll(listParams);
			listEnd.addAll(listMethod);
			listEnd.addAll(listToString);
			listEnd.add("");
			listEnd.add("}");

			File fout = new File(filename + className + ".java");
			FileOutputStream fos = new FileOutputStream(fout);
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
			for (String string : listEnd) {
				bw.write(string);
				bw.newLine();
			}
			bw.close();
		}
	}

	private String getDataType(String data_type, Set<String> listImport) {
		String dataType = "";
		switch (data_type) {
		case "datetime":
			dataType = "Date";
			listImport.add("import java.util.Date;");
			break;
		case "bigint":
			dataType = "Long";
			break;
		case "varchar":
			dataType = "String";
			break;
		case "decimal":
			dataType = "BigDecimal";
			listImport.add("import java.math.BigDecimal;");
			break;
		case "double":
			dataType = "Double";
			break;
		case "int":
			dataType = "Integer";
			break;
		case "tinyint":
			dataType = "Integer";
			break;
		case "smallint":
			dataType = "Integer";
			break;
		case "mediumint":
			dataType = "Integer";
			break;
		case "float":
			dataType = "Float";
			break;
		case "text":
			dataType = "String";
			break;
		case "blob":
			dataType = "byte[]";
			break;
		case "char":
			dataType = "String";
			break;
		case "date":
			dataType = "Date";
			break;
		case "timestamp":
			dataType = "Date";
			break;
		default:
			break;
		}
		return dataType;
	}

	private String convertJavaKey(String key, boolean capitalize) {
		String beanKey = toUnderscoreName(key);
		String[] strs = beanKey.toLowerCase().split("_");
		StringBuffer result = new StringBuffer();
		String preStr = "";
		for (int i = 0; i < strs.length; i++) {
			if (preStr.length() == 1) {
				result.append(strs[i]);
			} else {
				result.append(changeFirstCharacterCase(strs[i], true));
			}
			preStr = strs[i];
		}
		return changeFirstCharacterCase(result.toString(), capitalize);
	}

	private String changeFirstCharacterCase(String str, boolean capitalize) {
		if (str == null || str.length() == 0) {
			return str;
		}
		StringBuffer buf = new StringBuffer(str.length());
		if (capitalize) {
			buf.append(Character.toUpperCase(str.charAt(0)));
		} else {
			buf.append(Character.toLowerCase(str.charAt(0)));
		}
		buf.append(str.substring(1));
		return buf.toString();
	}

	private String toUnderscoreName(String name) {
		if (name == null)
			return null;

		String filteredName = name;
		if (filteredName.indexOf("_") >= 0 && filteredName.equals(filteredName.toUpperCase())) {
			filteredName = filteredName.toLowerCase();
		}
		if (filteredName.indexOf("_") == -1 && filteredName.equals(filteredName.toUpperCase())) {
			filteredName = filteredName.toLowerCase();
		}

		StringBuffer result = new StringBuffer();
		if (filteredName != null && filteredName.length() > 0) {
			result.append(filteredName.substring(0, 1).toLowerCase());
			for (int i = 1; i < filteredName.length(); i++) {
				String preChart = filteredName.substring(i - 1, i);
				String c = filteredName.substring(i, i + 1);
				if (c.equals("_")) {
					result.append("_");
					continue;
				}
				if (preChart.equals("_")) {
					result.append(c.toLowerCase());
					continue;
				}
				if (c.matches("\\d")) {
					result.append(c);
				} else if (c.equals(c.toUpperCase())) {
					result.append("_");
					result.append(c.toLowerCase());
				} else {
					result.append(c);
				}
			}
		}
		return result.toString();
	}
}
